package com.tsfyun.scm.service.impl.materiel;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Snowflake;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;
import com.tsfyun.common.base.dto.Result;
import com.tsfyun.common.base.enums.domain.DomainOprationEnum;
import com.tsfyun.common.base.enums.domain.MaterielStatusEnum;
import com.tsfyun.common.base.exception.ServiceException;
import com.tsfyun.common.base.help.excel.ExcelUtil;
import com.tsfyun.common.base.security.SecurityUtil;
import com.tsfyun.common.base.support.DomainStatus;
import com.tsfyun.common.base.util.StringUtils;
import com.tsfyun.common.base.util.TsfPreconditions;
import com.tsfyun.common.base.validator.ValidatorUtils;
import com.tsfyun.common.base.vo.ImpExcelVO;
import com.tsfyun.scm.client.CustomsCodeClient;
import com.tsfyun.scm.dto.materiel.ClassifyMaterielExpDTO;
import com.tsfyun.scm.dto.materiel.ExpMaterielQTO;
import com.tsfyun.scm.dto.materiel.MaterielExpDTO;
import com.tsfyun.scm.dto.materiel.MaterielExpQTO;
import com.tsfyun.scm.entity.customer.Customer;
import com.tsfyun.scm.entity.materiel.Materiel;
import com.tsfyun.scm.entity.materiel.MaterielExp;
import com.tsfyun.scm.entity.materiel.MaterielLog;
import com.tsfyun.scm.excel.ClassifyMaterielExcel;
import com.tsfyun.scm.excel.ClassifyMaterielExpExcel;
import com.tsfyun.scm.excel.MaterielExpExcel;
import com.tsfyun.scm.mapper.materiel.MaterielExpMapper;
import com.tsfyun.scm.service.customer.ICustomerService;
import com.tsfyun.scm.service.materiel.IMaterielExpService;
import com.tsfyun.common.base.extension.ServiceImpl;
import com.tsfyun.scm.service.materiel.IMaterielLogService;
import com.tsfyun.scm.service.order.IExpOrderMemberService;
import com.tsfyun.scm.stream.send.ScmProcessor;
import com.tsfyun.scm.system.vo.CustomsCodeVO;
import com.tsfyun.scm.system.vo.CustomsElementsVO;
import com.tsfyun.scm.util.MaterielUtil;
import com.tsfyun.scm.util.TsfWeekendSqls;
import com.tsfyun.scm.vo.materiel.MaterielExpHSVO;
import com.tsfyun.scm.vo.materiel.MaterielExpVO;
import com.tsfyun.scm.vo.materiel.SimpleMaterielVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import tk.mybatis.mapper.entity.Example;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 *
 * @since 2021-03-08
 */
@Service
@Slf4j
public class MaterielExpServiceImpl extends ServiceImpl<MaterielExp> implements IMaterielExpService {

    @Autowired
    private MaterielExpMapper materielExpMapper;
    @Autowired
    private ICustomerService customerService;
    @Autowired
    private CustomsCodeClient customsCodeClient;
    @Autowired
    private IMaterielLogService materielLogService;
    @Autowired
    private IExpOrderMemberService expOrderMemberService;
    @Autowired
    private ScmProcessor saasProcessor;
    @Autowired
    private Snowflake snowflake;

    @Override
    public Result<List<MaterielExpHSVO>> pageList(MaterielExpQTO qto) {
        PageHelper.startPage(qto.getPage(),qto.getLimit());
        Map<String,Object> params = beanMapper.map(qto,Map.class);
        List<MaterielExpVO> materielVOList = materielExpMapper.pageList(params);
        PageInfo page = new PageInfo<>(materielVOList);
        List<MaterielExpHSVO> hsvoList = formatMateriel(materielVOList);
        return Result.success((int)page.getTotal(),hsvoList);
    }

    @Override
    public List<MaterielExpHSVO> formatMateriel(List<MaterielExpVO> materielVOList) {
        List<MaterielExpHSVO> hsvoList = Lists.newArrayList();
        if(CollectionUtil.isNotEmpty(materielVOList)){
            Map<String, CustomsCodeVO> hsCodeMap = new LinkedHashMap<>();
            materielVOList.stream().forEach(vo ->{
                CustomsCodeVO codeVO = null;
                if(StringUtils.isNotEmpty(vo.getHsCode())){
                    codeVO = hsCodeMap.get(vo.getHsCode());
                    if(codeVO==null){
                        Result<CustomsCodeVO> codeVOResult = customsCodeClient.detail(vo.getHsCode());
                        if(codeVOResult.isSuccess()) {
                            codeVO = codeVOResult.getData();
                            hsCodeMap.put(vo.getHsCode(),codeVO);
                        }
                    }
                }
                hsvoList.add(new MaterielExpHSVO(vo,codeVO));
            });
        }
        return hsvoList;
    }

    //导入文件上限
    private static Integer uploadMaxmum = 10000;
    @Override
    public ImpExcelVO importMateriel(MultipartFile file) {
        ImpExcelVO excelVO = new ImpExcelVO();
        Stopwatch stopwatch = Stopwatch.createStarted();
        List<MaterielExpExcel> dataList = null;
        try {
            dataList = ExcelUtil.readExcel(file, MaterielExpExcel.class, 1);
        } catch (Exception e) {
            log.error("读取导入的excel文件异常",e);
            throw new ServiceException("导入数据异常，请仔细检查您的导入文件是否符合模板要求");
        }
        if(CollectionUtils.isEmpty(dataList)) {
            throw new ServiceException("本次没有可以导入的数据");
        }
        TsfPreconditions.checkArgument(dataList.size() <= uploadMaxmum,new ServiceException(String.format("一次最多导入%d条物料信息",uploadMaxmum)));
        List<MaterielExp> materielList = Lists.newArrayList();
        Map<String,Long> customerMap = new LinkedHashMap<>();
        LocalDateTime now = LocalDateTime.now();
        String person = SecurityUtil.getCurrentPersonIdAndName();
        for(int i=0;i<dataList.size();i++){
            MaterielExpExcel materielExcel = dataList.get(i);
            int contentRowNo = i + 2;
            ValidatorUtils.validateEntity(materielExcel,contentRowNo);
            MaterielExp materiel = new MaterielExp();

            if(Objects.isNull(customerMap.get(materielExcel.getCustomerName()))){
                Customer customer = customerService.findByName(materielExcel.getCustomerName());
                TsfPreconditions.checkArgument(Objects.nonNull(customer),new ServiceException(String.format("第%d行客户不存在",contentRowNo)));
                customerMap.put(materielExcel.getCustomerName(),customer.getId());
            }
            materiel.setCustomerId(customerMap.get(materielExcel.getCustomerName()));
            materiel.setModel(materielExcel.getModel().replace("型号","").replace("型",""));
            materiel.setBrand(materielExcel.getBrand().replace("品牌","").replace("牌","").toUpperCase());
            materiel.setName(materielExcel.getName());
            materiel.setSpec(materielExcel.getSpec());
            //待归类
            materiel.setStatusId(MaterielStatusEnum.WAIT_CLASSIFY.getCode());
            materiel.setSource("后台物料导入");
            materiel.setSourceMark("");
            materiel.setCreateBy(person);
            materiel.setUpdateBy(person);
            materiel.setDateCreated(now);
            materiel.setDateUpdated(now);
            materielList.add(materiel);
        }
        Integer successCount = materielExpMapper.batchSaveList(materielList);
        excelVO.setTotalCount(materielList.size());
        excelVO.setSuccessCount(successCount);
        stopwatch.stop();
        excelVO.setMessage(String.format("耗时：%d秒",stopwatch.elapsed(TimeUnit.SECONDS)));
        return excelVO;
    }

    // 已归类物料导入
    @Override
    public ImpExcelVO importClassifyMateriel(MultipartFile file) {
        ImpExcelVO excelVO = new ImpExcelVO();
        Stopwatch stopwatch = Stopwatch.createStarted();
        List<ClassifyMaterielExpExcel> dataList = null;
        try {
            dataList = ExcelUtil.readExcel(file, ClassifyMaterielExpExcel.class, 1);
        } catch (Exception e) {
            log.error("读取导入的excel文件异常",e);
            throw new ServiceException("导入数据异常，请仔细检查您的导入文件是否符合模板要求");
        }
        if(CollectionUtils.isEmpty(dataList)) {
            throw new ServiceException("本次没有可以导入的数据");
        }
        TsfPreconditions.checkArgument(dataList.size() <= uploadMaxmum,new ServiceException(String.format("一次最多导入%d条物料信息",uploadMaxmum)));
        Long personId = SecurityUtil.getCurrentPersonId();
        String personName = SecurityUtil.getCurrentPersonName();
        for(int i=0;i<dataList.size();i++){
            ClassifyMaterielExpExcel materielExcel = dataList.get(i);
            int contentRowNo = i + 2;
            ValidatorUtils.validateEntity(materielExcel,contentRowNo);
            //根据海关编码获取申报要素
            Result<List<CustomsElementsVO>> listResult = customsCodeClient.elementDetail(materielExcel.getHsCode());
            TsfPreconditions.checkArgument(listResult.isSuccess(),new ServiceException("获取申报要素失败，请稍后重试"));
            //验证申报要素
            List<String> elementsVal = Arrays.asList(StringUtils.removeSpecialSymbol(materielExcel.getElements()).concat(" ").split("\\|"));
            List<CustomsElementsVO> customsElements = listResult.getData();
            if(customsElements!=null&&customsElements.size()>0&&(elementsVal==null||customsElements.size()!=elementsVal.size())){
                throw new ServiceException(String.format("第%d行%s", contentRowNo, "申报要素错误"));
            }
            List<String> declareSpecs = new ArrayList<>();
            for(int j=0;j<customsElements.size();j++){
                CustomsElementsVO ce = customsElements.get(j);
                String evalue = StringUtils.removeSpecialSymbol(elementsVal.get(j));
                if(MaterielUtil.ELEMENT_BRAND_MARK.equals(ce.getName())){
                    // 品牌
                    evalue = "X-X-X牌";
                }else if(MaterielUtil.ELEMENT_MODEL_MARK.equals(ce.getName())){
                    // 型号
                    evalue = "X-X-X型";
                }
                declareSpecs.add(evalue);
            }
            String elements = String.join("|",declareSpecs)
                    .replace("X-X-X型",MaterielUtil.formatGoodsModel(materielExcel.getModel()))
                    .replace("X-X-X牌",MaterielUtil.formatGoodsBrand(materielExcel.getBrand()));
            materielExcel.setElements(elements);
            materielExcel.setPersonId(personId);
            materielExcel.setPersonName(personName);
        }
        List<List<ClassifyMaterielExpExcel>> subList = ListUtils.partition(dataList,500);
        subList.stream().forEach(sub ->{
            saasProcessor.sendClassifyMaterielExp(sub);
        });
        excelVO.setTotalCount(dataList.size());
        excelVO.setSuccessCount(dataList.size());
        stopwatch.stop();
        excelVO.setMessage(String.format("耗时：%d秒",stopwatch.elapsed(TimeUnit.SECONDS)));
        return excelVO;
    }

    @Override
    public void removeIdsNotPrompt(List<String> ids) {
        if(CollUtil.isEmpty(ids)){return;}
        ids.stream().forEach(id ->{
            try{
                TsfWeekendSqls sqls = TsfWeekendSqls.<MaterielExp>custom()
                        .andEqualTo(true,MaterielExp::getId,id)
                        .andNotEqualTo(true,MaterielExp::getStatusId,MaterielStatusEnum.CLASSED.getCode());
                materielExpMapper.deleteByExample(Example.builder(MaterielExp.class).where(sqls).build());
            }catch (Exception e){

            }
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public MaterielExpVO obtainMaterialAndSave(MaterielExpDTO dto) {
        String key = dto.getBrand().concat((dto.getCustomerId()!=null)?dto.getCustomerId().toString():"").intern();
        synchronized (key){
            MaterielExpVO materielVO = materielExpMapper.findByKey(dto.getCustomerId(),dto.getModel(),dto.getBrand(),dto.getName(),dto.getSpec());
            if(Objects.isNull(materielVO)){
                MaterielExp materiel = new MaterielExp();
                materiel.setName(dto.getName());
                //待归类
                materiel.setStatusId(MaterielStatusEnum.WAIT_CLASSIFY.getCode());
                materiel.setCustomerId(dto.getCustomerId());
                materiel.setId(StringUtils.UUID());
                materiel.setModel(dto.getModel());
                materiel.setBrand(dto.getBrand().toUpperCase());
                materiel.setSpec(dto.getSpec());
                materiel.setSource(dto.getSource());
                materiel.setSourceMark(dto.getSourceMark());
                try{
                    super.saveNonNull(materiel);
                }catch (DuplicateKeyException e) {
                    if(e.getMessage().contains("UK_materiel_exp_customer_id_materiel")) {
                        throw new ServiceException(String.format("客户物料已经存在[%s][%s][%s][%s]",materiel.getName(),materiel.getBrand(),materiel.getModel(),materiel.getSpec()));
                    } else {
                        throw new ServiceException("物料保存失败，请检查数据是否填写正确");
                    }
                }
                materielVO = beanMapper.map(materiel,MaterielExpVO.class);
            }
            return materielVO;
        }
    }

    @Override
    public Map<String, Object> classifyDetail(List<String> ids) {
        List<MaterielExp> materielList = materielExpMapper.findInId(ids);
        TsfPreconditions.checkArgument(CollectionUtil.isNotEmpty(materielList),new ServiceException("未找到对应物料信息"));
        MaterielExp materiel = materielList.get(0);
        //验证状态是否可以执行操作
        DomainStatus.getInstance().check(DomainOprationEnum.MATERIEL_CLASSIFY_EXP, materiel.getStatusId());
        Map<String,Object> materielMap = beanMapper.map(materiel,Map.class);
        List<String> hsCodeList = Lists.newArrayList();
        if(materielList.size()>1){
            for(int i=1;i<materielList.size();i++){
                MaterielExp om = materielList.get(i);
                //验证状态是否可以执行操作
                DomainStatus.getInstance().check(DomainOprationEnum.MATERIEL_CLASSIFY_EXP, om.getStatusId());
                materielMap.put("id",StringUtils.removeSpecialSymbol(materielMap.get("id")).concat(",").concat(om.getId()));
                materielMap.put("model",StringUtils.removeSpecialSymbol(materielMap.get("model")).concat("|").concat(om.getModel()));
                materielMap.put("brand",StringUtils.removeSpecialSymbol(materielMap.get("brand")).concat("|").concat(om.getBrand()));
                if(StringUtils.isNotEmpty(om.getHsCode())&&!hsCodeList.contains(om.getHsCode())){
                    hsCodeList.add(om.getHsCode());
                }
            }
        }
        //构建申报要素
        List<CustomsElementsVO> elements = Lists.newArrayList();
        if(StringUtils.isNotEmpty(materiel.getHsCode())){
            if(!hsCodeList.contains(materiel.getHsCode())){
                hsCodeList.add(materiel.getHsCode());
            }
            //根据海关编码获取申报要素
            Result<List<CustomsElementsVO>> listResult = customsCodeClient.elementDetail(materiel.getHsCode());
            TsfPreconditions.checkArgument(listResult.isSuccess(),new ServiceException("获取申报要素失败，请稍后重试"));
            elements = listResult.getData();
            List<String> valElements = Arrays.asList(StringUtils.removeSpecialSymbol(materiel.getElements()).concat(" ").split("\\|"));
            for(int i=0;i<elements.size();i++){
                CustomsElementsVO ce = elements.get(i);
                String evalue = StringUtils.removeSpecialSymbol((valElements.size()>i)?valElements.get(i):"");
                if(MaterielUtil.ELEMENT_BRAND_MARK.equals(ce.getName())){
                    // 品牌
                    evalue = MaterielUtil.formatGoodsBrand(materiel.getBrand());
                }else if(MaterielUtil.ELEMENT_MODEL_MARK.equals(ce.getName())){
                    // 型号
                    evalue = MaterielUtil.formatGoodsModel(materiel.getModel());
                }
                ce.setVal(evalue);
            }
        }
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("materiel",materielMap);
        result.put("elements",elements);
        result.put("hsCodeNum",hsCodeList.size());
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveClassify(ClassifyMaterielExpDTO dto) {
        List<String> ids = Arrays.asList(dto.getId().split(","));
        List<MaterielExp> materielList = materielExpMapper.findInId(ids);
        TsfPreconditions.checkArgument(CollectionUtil.isNotEmpty(materielList),new ServiceException("未找到对应物料信息"));
        //根据海关编码获取申报要素
        Result<List<CustomsElementsVO>> listResult = customsCodeClient.elementDetail(dto.getHsCode());
        TsfPreconditions.checkArgument(listResult.isSuccess(),new ServiceException("获取申报要素失败，请稍后重试"));
        List<CustomsElementsVO> customsElements = listResult.getData();
        List<String> elementsVal = dto.getElementsVal();//填写的申报要素
        if(customsElements!=null&&customsElements.size()>0&&(elementsVal==null||customsElements.size()!=elementsVal.size())){
            TsfPreconditions.checkArgument(false,new ServiceException("申报要素错误，请刷新页面重试"));
        }
        List<String> declareSpecs = new ArrayList<>();
        for(int i=0;i<customsElements.size();i++){
            CustomsElementsVO ce = customsElements.get(i);
            String evalue = StringUtils.removeSpecialSymbol(elementsVal.get(i));
            if(MaterielUtil.ELEMENT_BRAND_MARK.equals(ce.getName())){
                // 品牌
                evalue = "X-X-X牌";
            }else if(MaterielUtil.ELEMENT_MODEL_MARK.equals(ce.getName())){
                // 型号
                evalue = "X-X-X型";
            }
            declareSpecs.add(evalue);
        }
        Date now = new Date();
        String personName = SecurityUtil.getCurrentPersonName();
        materielList.stream().forEach(ma ->{
            //验证状态是否可以执行操作
            DomainStatus.getInstance().check(DomainOprationEnum.MATERIEL_CLASSIFY_EXP, ma.getStatusId());

            //记录历史物料信息
            MaterielExp history = new MaterielExp();
            BeanUtils.copyProperties(ma,history);

            ma.setName(dto.getName());
            // 品牌转大写
            if(materielList.size()==1){
                ma.setBrand(dto.getBrand().toUpperCase());
                ma.setSpec(dto.getSpec());
            }
            ma.setHsCode(dto.getHsCode());

            String elements = String.join("|",declareSpecs)
                    .replace("X-X-X型",MaterielUtil.formatGoodsModel(ma.getModel()))
                    .replace("X-X-X牌",MaterielUtil.formatGoodsBrand(ma.getBrand()));
            ma.setElements(elements);
            ma.setMemo(dto.getMemo());
            ma.setStatusId(MaterielStatusEnum.CLASSED.getCode());
            ma.setClassifyPerson(personName);
            ma.setClassifyTime(now);
            //去除重复产品
            removeRepeat(ma);
            //保存归类
            super.updateById(ma);
            //记录数据变更
            materielLogService.recordMaterielLog(history,ma);
        });
    }

    //去除重复物料
    @Transactional(rollbackFor = Exception.class)
    public void removeRepeat(MaterielExp materiel){
        List<MaterielExp> materielList = materielExpMapper.findByRepeat(materiel);
        if(CollUtil.isNotEmpty(materielList)){
            List<String> ids = Lists.newArrayList();
            materielList.stream().forEach(um ->{
                //修改出口订单明细
                expOrderMemberService.updateMemberMaterielId(um.getId(),materiel.getId());
                //修改归类日志
                materielLogService.updateMaterielId(um.getId(),materiel.getId());
                ids.add(um.getId());
            });
            //删除重复物料
            removeByIds(ids);
        }
    }

    @Override
    public void saveImportClassifyMateriel(List<ClassifyMaterielExpExcel> classifyList) {
        if(CollUtil.isEmpty(classifyList)){
            return;
        }
        List<MaterielExp> materielList = new ArrayList<>();
        List<MaterielLog> materielLogList = new ArrayList<>();
        Date nowDate = new Date();
        LocalDateTime nowLocalDate = LocalDateTime.now();
        classifyList.stream().forEach(cl ->{
            List<MaterielExp> materiels = materielExpMapper.findByMateriel(cl.getModel(),cl.getBrand(),cl.getName(),cl.getSpec());
            if(CollectionUtils.isEmpty(materiels)){
                materiels = new ArrayList<>();
                MaterielExp materiel = new MaterielExp();
                materiel.setCustomerId(Long.valueOf(0));//未知客户
                materiel.setSource("已归类物料导入");
                materiels.add(materiel);
            }
            materiels.stream().forEach(materiel ->{
                //记录历史物料信息
                MaterielExp history = new MaterielExp();
                BeanUtils.copyProperties(materiel,history);
                materiel.setHsCode(cl.getHsCode());
                materiel.setModel(cl.getModel());
                materiel.setBrand(cl.getBrand());
                materiel.setName(cl.getName());
                materiel.setSpec(cl.getSpec());
                materiel.setElements(cl.getElements());
                materiel.setMemo(cl.getMemo());
                materiel.setStatusId(MaterielStatusEnum.CLASSED.getCode());
                materiel.setClassifyPerson(cl.getPersonName());
                materiel.setClassifyTime(nowDate);
                materiel.setCreateBy(StringUtils.removeSpecialSymbol(cl.getPersonId()).concat("/").concat(StringUtils.isEmpty(cl.getPersonName())?"":cl.getPersonName()));
                materiel.setDateCreated(nowLocalDate);
                materiel.setUpdateBy(materiel.getCreateBy());
                materiel.setDateUpdated(nowLocalDate);
                if(Objects.isNull(materiel.getId())){
                    materiel.setId(StringUtils.UUID());
                }else{
                    //修改记录日志
                    String change = materielLogService.comparativeChange(history,materiel);
                    if(StringUtils.isNotEmpty(change)){
                        MaterielLog log = new MaterielLog();
                        log.setId(snowflake.nextId());
                        log.setChangeContent(change);
                        log.setMaterielId(materiel.getId());
                        log.setCreateBy(materiel.getCreateBy());
                        log.setDateCreated(nowLocalDate);
                        log.setUpdateBy(log.getCreateBy());
                        log.setDateUpdated(nowLocalDate);
                        materielLogList.add(log);
                    }
                }
                materielList.add(materiel);
            });
        });
        materielExpMapper.batchSaveClassifyList(materielList);
        if(CollUtil.isNotEmpty(materielLogList)){
            materielLogService.batchSave(materielLogList);
        }
    }

    @Override
    public List<SimpleMaterielVO> vague(ExpMaterielQTO qto) {
        Map<String,Object> params = beanMapper.map(qto,Map.class);
        return materielExpMapper.vague(params);
    }

    @Override
    @Transactional
    public void removeMateriel(String id) {
        try{
            materielLogService.removeByMaterielId(id);
            removeById(id);
        }catch (Exception e){
            throw new ServiceException("当前产品信息已被订单引用无法删除");
        }
    }
}
